package com.wangjiang.mario.admin.system.domain.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wangjiang.mario.admin.common.annotation.DataScope;
import com.wangjiang.mario.admin.common.base.BaseServiceImpl;
import com.wangjiang.mario.admin.common.exception.LoginErrorException;
import com.wangjiang.mario.admin.common.exception.MoreThanOneResultException;
import com.wangjiang.mario.admin.common.exception.NoFoundException;
import com.wangjiang.mario.admin.common.exception.RegisterFailedException;
import com.wangjiang.mario.admin.common.output.MPage;
import com.wangjiang.mario.admin.common.util.BcryptUtil;
import com.wangjiang.mario.admin.common.util.CommonUtils;
import com.wangjiang.mario.admin.system.domain.entity.*;
import com.wangjiang.mario.admin.system.domain.mapper.*;
import com.wangjiang.mario.admin.system.domain.service.JwtService;
import com.wangjiang.mario.admin.system.domain.service.UserService;
import com.wangjiang.mario.admin.system.object.token.TokenResult;
import com.wangjiang.mario.admin.system.object.user.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;


@Service
public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserEntity>
        implements UserService {

    private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);
    private static final String NO_CHANGE_PASSWORD_FLAG = "origin";

    @Autowired
    private JwtService jwtService;

    @Autowired
    private UserDeptMapper userDeptMapper;

    @Autowired
    private UserPositionMapper userPositionMapper;

    @Autowired
    private UserRoleMapper userRoleMapper;

    @Autowired
    private RoleMenuMapper roleMenuMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean register(UserRegisterDto user) {
        // 检测用户名是否被占用
        if (checkIfRegister(user)) {
            throw new RegisterFailedException("注册失败：用户名，邮箱或手机号码重复");
        }
        return doRegister(user);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateRegisterUser(UserRegisterDto userRegisterDto) {
        if (checkIfRegister(userRegisterDto)) {
            throw new RegisterFailedException("注册失败：用户名，邮箱或手机号码重复");
        }
        return doRegister(userRegisterDto);
    }

    private Boolean doRegister(UserRegisterDto user) {
        UserEntity userEntity = new UserEntity();
        BeanUtils.copyProperties(user, userEntity);

        if (!NO_CHANGE_PASSWORD_FLAG.equals(user.getPassword())) {
            String password = BcryptUtil.EncodeByBcrypt(user.getPassword());
            userEntity.setPassword(password);
        } else {
            userEntity.setPassword(null);
        }

        Boolean ret = baseSave(userEntity);
        // 注册部门和岗位
        if (CommonUtils.isNotNull(user.getDeptCode())) {
            saveUserDept(userEntity.getUuid(), user.getDeptCode());
        }
        // 岗位
        if (CommonUtils.isNotNull(user.getPositionCode())) {
            saveUserPosition(userEntity.getUuid(), user.getPositionCode());
        }
        // 角色
        if (CommonUtils.isNotNull(user.getRoleCode())) {
            saveUserRoles(userEntity.getUuid(), user.getRoleCode());
        }
        if (!ret) {
            throw new RegisterFailedException();
        }
        return true;
    }


    @Transactional(rollbackFor = Exception.class)
    protected Integer saveUserRoles(String uuid, String[] roleCode) {
        deleteOldUserRoles(uuid);
        List<UserRoleEntity> list = new ArrayList<>();
        if (roleCode.length > 0) {
            for (String code : roleCode) {
                UserRoleEntity ur = new UserRoleEntity();
                ur.setRoleCode(code);
                ur.setUserUuid(uuid);
                list.add(ur);
            }
            return userRoleMapper.batchInsert(list);
        }
        return 0;
    }

    private Integer deleteOldUserRoles(String uuid) {
        return userRoleMapper.delete(new QueryWrapper<UserRoleEntity>().eq("user_uuid", uuid));
    }

    /**
     * 添加用户岗位关联
     *
     * @param uuid
     * @param positionCode
     */
    @Transactional(rollbackFor = Exception.class)
    protected Integer saveUserPosition(String uuid, String positionCode) {
        UserPositionEntity up = new UserPositionEntity();
        up.setPositionCode(positionCode);
        up.setUserId(uuid);
        // 删除旧的职位关联关系
        deleteOldUserPosition(uuid);
        return userPositionMapper.insert(up);
    }

    private Integer deleteOldUserPosition(String uuid) {
        return userPositionMapper.delete(new QueryWrapper<UserPositionEntity>().eq("user_id", uuid));
    }

    /**
     * 添加用户和部门的管理数据
     *
     * @param uuid
     * @param deptCode
     */
    @Transactional(rollbackFor = Exception.class)
    protected Integer saveUserDept(String uuid, String deptCode) {
        UserDeptEntity ud = new UserDeptEntity();
        ud.setUserId(uuid);
        ud.setDeptCode(deptCode);
        // 需要删除旧的部门绑定，然后重新生成关系
        deleteOldUserDept(uuid);
        return userDeptMapper.insert(ud);
    }

    private Integer deleteOldUserDept(String uuid) {
        return userDeptMapper.delete(new QueryWrapper<UserDeptEntity>().eq("user_id", uuid));
    }

    /**
     * 登录
     *
     * @param userLoginDto
     * @return
     */
    @Override
    public TokenResult login(UserLoginDto userLoginDto) {
        // 1. 检查用户密码是否正确
        // 用户名可以是登录名，手机或者邮箱
        // 2. 生成token 返回
        TokenResult tokenResult = null;
        UserEntity user = doLogin(userLoginDto);
        if (CommonUtils.isNotNull(user)) {
            // 将用户uuid，用户其他资料存入token中，
            // uuid作为sub存储，其他资料作为map存入到body中
            tokenResult = jwtService.generateTokenByUserEntity(user);
        }

        return tokenResult;
    }


    private UserEntity findByLoginName(String loginName) {
        return baseMapper.findByLoginName(loginName);
    }

    private Integer findCountByLoginName(String loginName) {
        return baseMapper.findCountByLoginName(loginName);
    }

    @Override
    public UserEntity findUserByLoginName(String loginName) {
        Integer count = findCountByLoginName(loginName);
        UserEntity user;
        if (count == 1) {
            logger.info("found a match object");
            user = findByLoginName(loginName);
        } else if (count > 1) {
            logger.error("too many same user:{}, count:{}", loginName, count);
            throw new MoreThanOneResultException("too many same user");
        } else {
            logger.error("no such user");
            throw new NoFoundException("no such user");
        }
        return user;
    }

    @Override
    public UserRoleMenuVo getMe() {
        String uuid = jwtService.getUUid();
        UserRoleMenuVo user = getUserWithRoleAndMenuByUUid(uuid);
        return user;
    }

    @Override
    public Boolean update(UserEntity userEntity) {
        if (CommonUtils.isNotNull(userEntity) && CommonUtils.isNotNull(userEntity.getPassword())) {
            userEntity.setPassword(BcryptUtil.EncodeByBcrypt(userEntity.getPassword()));
        }


        return baseUpdateById(userEntity);
    }

    /**
     * 登出
     *
     * @return
     */
    @Override
    public Boolean logout() {
        return jwtService.removeCurrentToken();
    }

    /**
     * 关联查询分页
     *
     * @param currentPage
     * @param pageSize
     * @param qo
     * @param userVoClass
     * @return
     */
    @Override
    @DataScope(deptAlias = "dept")
    public MPage<UserVo> relatedPage(Integer currentPage, Integer pageSize, UserQo qo, Class<UserVo> userVoClass) {
        IPage page = new Page<UserVo>(currentPage - 1, pageSize);
        Page<UserVo> ret = baseMapper.relatedPage(page, qo);
        // 查询出角色列表
        List<UserVo> list = ret.getRecords();

        if (list.size() > 0) {
            List<String> userUUidList = list.stream().map(UserVo::getUuid).collect(Collectors.toList());
            List<UserRoleVo> userRoleVos = userRoleMapper.getUserRoles(userUUidList);
            Map<String, List<UserRoleVo>> userRoleVoMap = userRoleVos.stream().collect(Collectors.groupingBy(UserRoleVo::getUserUUid));
            for (UserVo user : list) {
                if (userRoleVoMap.containsKey(user.getUuid())) {
                    List<UserRoleVo> roleVoList = userRoleVoMap.get(user.getUuid());
                    if (CommonUtils.isNull(roleVoList)) roleVoList = new ArrayList<>();
                    user.setRoles(roleVoList);
                    user.setRoleCode(roleVoList.stream().map(UserRoleVo::getCode).collect(Collectors.toList()));
                }
            }
        }
        return pageFormat(new MPage(), ret);
    }

    /**
     * 根据用户的uuid获取用户及角色和菜单（权限）列表
     *
     * @param uuid
     * @return
     */
    @Override
    public UserRoleMenuVo getUserWithRoleAndMenuByUUid(String uuid) {
        List<UserRoleVo> userRoleVos = userRoleMapper.getRolesByUserUUid(uuid);
        Set<String> roles = new HashSet<>(userRoleVos.stream().map(UserRoleVo::getCode).collect(Collectors.toList()));
        List<RoleMenuEntity> userMenus = roleMenuMapper.getRoleMenusByUserUUid(uuid);
        List<String> perms = new ArrayList<>(userMenus.stream().filter(item -> item.getMenuType().equals(RoleMenuEntity.PERM_MENU)).map(RoleMenuEntity::getMenuCode).collect(Collectors.toSet()));
        List<String> menus = new ArrayList<>(userMenus.stream().filter(item -> item.getMenuType().equals(RoleMenuEntity.MENU_MENU)).map(RoleMenuEntity::getMenuCode).collect(Collectors.toSet()));
        UserEntity user = baseMapper.findRelatedByUUid(uuid);
        UserRoleMenuVo userRoleMenu = new UserRoleMenuVo();
        BeanUtils.copyProperties(user, userRoleMenu);
        userRoleMenu.setRoleList(userRoleVos);
        userRoleMenu.setPerms(perms);
        if(userRoleMenu.isAdmin()) {
            // 如果是admin 拥有所有权限
            userRoleMenu.setIsAdmin(true);
            // 这里通配符 前台有问题
            menus.add("*"); // 通配符，代表拥有所有菜单权限
        } else {
            userRoleMenu.setIsAdmin(false);
        }
        userRoleMenu.setMenus(menus);
        userRoleMenu.setRoles(roles);
        return userRoleMenu;
    }

    /**
     * 检查用户登录是否正确
     * 用户
     *
     * @param userLoginDto
     * @return
     */
    private UserEntity doLogin(UserLoginDto userLoginDto) {
        UserEntity user = findByLoginName(userLoginDto.getUsername());
        if (CommonUtils.isNull(user)) {
            throw new NoFoundException("没有该账户");
        }
        if (!BcryptUtil.checkpw(userLoginDto.getPassword(), user.getPassword())) {
            throw new LoginErrorException("密码错误");
        }
        return user;
    }


    /**
     * 检查是否被注册
     *
     * @param user
     * @return
     */
    private Boolean checkIfRegister(UserRegisterDto user) {
        QueryWrapper<UserEntity> queryWrapper = getDefaultWrapper();
        if (CommonUtils.isNotNull(user.getUuid())) {
            queryWrapper.ne("uuid", user.getUuid());
        }
        // 相同登录名
        Integer sameLoginNameNum = count(queryWrapper.clone().eq("login_name", user.getLoginName()));
        if (sameLoginNameNum > 0) {
            return true;
        }
        // 相同手机号码
        Integer samePhoneNum = count(queryWrapper.clone().eq("phone", user.getPhone()));
        if (samePhoneNum > 0) {
            return true;
        }

        // 相同邮箱
        Integer sameEmailNum = count(queryWrapper.clone().eq("email", user.getEmail()));
        if (sameEmailNum > 0) {
            return true;
        }

        return false;
    }
}
